<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>14.3.1-原生 DOM 的过渡</title>
</head>
<style>
    .box {
        width: 100px;
        height: 100px;
        background-color: skyblue;
    }
    .enter-from{
        transform: translateX(200px);
    }
    .enter-to{
        transform: translateX(0);
    }
    .enter-active{
        transition: transform 1s ease-in-out;
    }
    /* 初始状态 */
    .leave-from {
        transform: translateX(0);
    }
    /* 结束状态 */
    .leave-to {
        transform: translateX(200px);
    }
    /* 过渡过程 */
    .leave-active {
        transition: transform 2s ease-out;
    }
</style>
<body>
    <!-- <div class="box"></div> -->
</body>
    <script>

        /**
         * 如何为原生 DOM 创建过渡动效
         * - 过渡效果本质上是一个 DOM 元素在两种状态间的切换，
         *    浏览器会根据过渡效果自行完成 DOM 元素的过渡
         * - 这里的过渡效果指的是持续时长、运动曲线、要过渡的属性等
         * 
         * 使用 requestAnimationFrame 注册了一个回调函数
         * - 该回调函数理论上会在下一帧执行
         * - 浏览器就会在当前帧绘制元素的初始状态，然后在下一帧切换元素的状态，从而使得过渡生效
         * 
         * Chrome 或Safari 浏览器中运行上面这段代码，会发现过渡仍未生效
         * - 该 bug 的具体描述参见 Issue 675795: 
         *    Interop: mismatch in when animations are started between different browsers
         * - 其大意是，使用 requestAnimationFrame 函数注册回调会在当前帧执行，
         *    除非其他代码已经调用了一次 requestAnimationFrame 函数
         * - 通过嵌套一层 requestAnimationFrame 函数的调用即可解决上述问题
         * 
         * 完成整个进场过渡的实现
         * - beforeEnter 阶段：添加 enter-from 和 enter-active 类。
         * - enter 阶段：在下一帧中移除 enter-from 类，添加 enter-to。
         * - 进场动效结束：移除 enter-to 和 enter-active 类
         * 
         * 讨论 DOM 元素的离场过渡效果
         * - 与进场过渡一样，我们需要定义离场过渡的初始状态、结束状态以及过渡过程
         * - 当元素被卸载时，不要将其立即卸载，而是等待过渡效果结束后再卸载它
         * 
        */

        // 创建 class 为 box 的 DOM 元素
        const el = document.createElement('div');
        el.classList.add('box');

        // 在 DOM 元素被添加到页面之前，将初始状态和运动过程定义到元素上
        el.classList.add('enter-from');    // 初始状态
        el.classList.add('enter-active');  // 运动过程
        // 将元素添加到页面
        document.body.appendChild(el);
        
        // 在下一帧切换元素的状态
        requestAnimationFrame(() => {
            requestAnimationFrame(() => {
                // 切换元素的状态
                el.classList.remove('enter-from');  // 移除 enter-from
                el.classList.add('enter-to');       // 添加 enter-to
                
                // 监听 transitionend 事件完成收尾工作
                el.addEventListener('transitionend', () => {
                    el.classList.remove('enter-to');
                    el.classList.remove('enter-active');
                })
            })
        })
        
        // 卸载元素
        el.addEventListener('click', () => {
            // 将卸载动作封装到 performRemove 函数中
            const performRemove = () => el.parentNode.removeChild(el)

            // 设置初始状态：添加 leave-from 和 leave-active 类
            el.classList.add('leave-from')
            el.classList.add('leave-active')

            // 强制 reflow：使初始状态生效
            // document.body.offsetHeight

            // 在下一帧切换状态
            // requestAnimationFrame(() => {
                requestAnimationFrame(() => {
                    // 切换到结束状态
                    el.classList.remove('leave-from')
                    el.classList.add('leave-to')

                    // 监听 transitionend 事件做收尾工作
                    el.addEventListener('transitionend', () => {
                        el.classList.remove('leave-to')
                        el.classList.remove('leave-active')
                        // 当过渡完成后，记得调用 performRemove 函数将 DOM 元素移除
                        performRemove()
                    })
                })
            // })
        })


    </script>
    

</html>